package br.com.naybee.framework.client.utils;

import static com.google.gwt.json.client.JSONBoolean.getInstance;
import static java.lang.Boolean.parseBoolean;

import java.util.Date;
import java.util.Map.Entry;
import java.util.Set;
import static br.com.naybee.framework.client.utils.NumberUtils.*;
import br.com.naybee.framework.client.models.ListModel;
import br.com.naybee.framework.client.models.Model;
import br.com.naybee.framework.client.models.Models;
import static br.com.naybee.framework.client.utils.ValidatorUtils.*;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONBoolean;
import com.google.gwt.json.client.JSONNull;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;

public class JsonUtils {

	public static String toJson(Model model) {
		if (notEmpty(model)) {

			JSONObject jsonObj = new JSONObject();
			for (Entry<String, Object> entry : model.entrySet()) {
				jsonObj.put(entry.getKey(), toJsonValue(entry.getValue()));

			}
			return jsonObj.toString();
		}
		return "";
	}

	public static Model toModel(String json) {
		if (notEmpty(json)) {
			JSONValue jsonValue = parseStrict(json);
			return getModel(jsonValue.isObject());
		}
		return null;
	}

	private static JSONValue parseStrict(String json) {
		return JSONParser.parseStrict(json);
	}

	private static Model getModel(JSONObject object) {

		Model model = Models.newModel();
		Set<String> set = object.keySet();

		for (String key : set) {

			JSONValue jsonValue = object.get(key);

			if (jsonValue.isNull() instanceof JSONNull)
				model.add(key, null);
			else if (jsonValue.isBoolean() instanceof JSONBoolean)
				model.add(key, jsonValue.isBoolean().booleanValue());
			else if (jsonValue.isNumber() instanceof JSONNumber)
				/*
				 * fiz isso para retornar o valor verdadeiro que veio no model,
				 * em vez de retornar um double. Fabrício.
				 */
				model.add(key, jsonValue.isNumber().toString());
			else if (jsonValue.isString() instanceof JSONString)
				model.add(key, jsonValue.isString().stringValue());
			else if (jsonValue.isObject() instanceof JSONObject)
				model.add(key, getModel(jsonValue.isObject()));
			else if (jsonValue.isArray() instanceof JSONArray)
				model.add(key, getListModel(jsonValue.isArray()));
		}
		return model;
	}

	private static JSONValue toJsonValue(Object object) {
		if (isNull(object))
			return JSONNull.getInstance();
		else if (object instanceof Model)
			return new JSONString(toJson((Model) object));
		else if (object instanceof ListModel) {
			return toJsonArray((ListModel) object);
		} else if (object instanceof Boolean)
			return getInstance(parseBoolean(object.toString()));
		else if (object instanceof Number)
			return new JSONNumber(toDouble(object.toString()));
		else if (object instanceof String)
			return new JSONString(object.toString());
		else if (object instanceof Date) {
			Date date = (Date) object;
			return new JSONNumber(date.getTime());
		} else
			return new JSONString(object.toString());
	}

	public static ListModel toListModel(String json) {
		if (notEmpty(json)) {
			JSONValue jsonValue = parseStrict(json);
			return getListModel(jsonValue.isArray());
		}
		return null;
	}

	public static String toJson(ListModel listModel) {

		JSONArray array = new JSONArray();

		for (int i = 0; i < listModel.size(); i++) {
			array.set(i, toJsonValue(listModel.get(i)));
		}

		return array.toString();
	}

	private static ListModel getListModel(JSONArray jsonArray) {
		ListModel listModel = null;

		for (int i = 0; i < jsonArray.size(); i++) {

			if (isNull(listModel))
				listModel = Models.newListModel();

			JSONValue jsonValue = jsonArray.get(i);

			if (jsonValue.isObject() instanceof JSONObject)
				listModel.add(getModel(jsonValue.isObject()));
		}
		return listModel;
	}

	private static JSONArray toJsonArray(ListModel listModel) {
		JSONArray array = new JSONArray();

		for (int i = 0; i < listModel.size(); i++) {
			array.set(i, toJsonValue(listModel.get(i)));
		}

		return array;

	}
}
